#pragma once
//封装日志宏,通过日志宏进行日志打印，同时打印信息前带有系统时间以及文件名和行号
//[16:44:07][log.cpp:22]打开文件失败

#include <iostream>
#include <cstdarg>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
using namespace std;

namespace ns_log
{
    enum
    {
        Debug = 0,
        Info,
        Warning,
        Error,
        Fatal
    };

    enum
    {
        Screen = 10,
        OneFile,
        ClassFile
    };

    string LevelToString(int level)
    {
        switch (level)
        {
        case Debug:
            return "Debug";
        case Info:
            return "Info";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
            
        default:
            return "Unknown";
        }
    }

    const int default_style = Screen;
    const string default_filename = "Log.";
    const string logdir = "log";

    class Log
    {
    public:
        Log(int style = default_style, string filename = default_filename)
            : _style(style), _filename(filename)
        {
            if (_style != Screen)
                mkdir(logdir.c_str(), 0775);
        }

        // 更改打印方式
        void Enable(int style)
        {
            _style = style;
            if (_style != Screen)
                mkdir(logdir.c_str(), 0775);
        }

        // 时间戳转化为年月日时分秒
        string GetTime()
        {
            time_t currtime = time(nullptr);
            struct tm *curr = localtime(&currtime);
            char time_buffer[128];
            snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
                     curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday, curr->tm_hour, curr->tm_min, curr->tm_sec);
            return time_buffer;
        }

        // 写入到文件中
        void WriteLogToOneFile(const string &logname, const string &message)
        {
            FILE *fp = fopen(logname.c_str(), "a");
            if (fp == nullptr)
            {
                perror("fopen failed");
                exit(-1);
            }
            fprintf(fp, "%s\n", message.c_str());

            fclose(fp);
        }

        // 打印日志
        void WriteLogToClassFile(const string &levelstr, const string &message)
        {
            string logname = logdir;
            logname += "/";
            logname += _filename;
            logname += levelstr;
            WriteLogToOneFile(logname, message);
        }

        void WriteLog(const string &levelstr, const string &message)
        {
            switch (_style)
            {
            case Screen:
                cout << message << endl; // 打印到屏幕中
                break;
            case OneFile:
                WriteLogToClassFile("all", message); // 给定all，直接写到all里
                break;
            case ClassFile:
                WriteLogToClassFile(levelstr, message); // 写入levelstr里
                break;
            default:
                break;
            }
        }

        // 提供接口给运算符重载使用
        void _LogMessage(int level, const char *file, int line, char *rightbuffer)
        {
            char leftbuffer[1024];
            string levelstr = LevelToString(level);
            string currtime = GetTime();
            string idstr = to_string(getpid());

            snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s][%s:%d]", levelstr.c_str(), currtime.c_str(), idstr.c_str(), file, line);

            string messages = leftbuffer;
            messages += rightbuffer;
            WriteLog(levelstr, messages);
        }

        // 运算符重载
        void operator()(int level, const char *file, int line, const char *format, ...)
        {
            char rightbuffer[1024];
            va_list args;                                              // va_list 是指针
            va_start(args, format);                                    // 初始化va_list对象，format是最后一个确定的参数
            vsnprintf(rightbuffer, sizeof(rightbuffer), format, args); // 写入到rightbuffer中
            va_end(args);
            _LogMessage(level, file, line, rightbuffer);
        }

        ~Log()
        {
        }

    private:
        int _style;
        string _filename;
    };

    Log lg;

    class Conf
    {
    public:
        Conf()
        {
            lg.Enable(Screen);
        }
        ~Conf()
        {
        }
    };

    Conf conf;
}

// 辅助宏
#define lg(level, format, ...) ns_log::lg(level, __FILE__, __LINE__, format, ##__VA_ARGS__)